package webx.http;

import stdx.Utils;
import java.util.Map;

import stdx.BaseException;
import java.io.IOException;
import java.io.InputStream;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.entity.StringEntity;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;

import org.apache.http.util.EntityUtils;
import org.apache.http.util.ByteArrayBuffer;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

public class Http{
	private static int timeout = 1000;
	private static String charset = "UTF-8";
	private static ThreadLocal<Integer> errno = new ThreadLocal<Integer>();
	private static ThreadLocal<Integer> status = new ThreadLocal<Integer>();

	public static int GetStatus(){
		return status.get();
	}
	public static int GetTimeout(){
		return timeout;
	}
	public static int GetErrorCode(){
		return errno.get();
	}
	public static String GetCharset(){
		return charset;
	}
	public static void SetTimeout(int ms){
		timeout = ms;
	}
	public static void SetCharset(String name){
		charset = name;
	}

	public static String Encode(String msg){
		return Encode(msg, charset);
	}
	public static String Decode(String msg){
		return Decode(msg, charset);
	}
	public static String Encode(String msg, String charset){
		String res = msg;

		try{
			res = java.net.URLEncoder.encode(msg, charset);
		}
		catch(Exception e){
		}

		return res;
	}
	public static String Decode(String msg, String charset){
		String res = msg;

		try{
			res = java.net.URLDecoder.decode(msg, charset);
		}
		catch(Exception e){
		}
		
		return res;
	}

	public static SSLContext CreateIgnoreVerifySSL() throws Exception{
		SSLContext sc = SSLContext.getInstance("SSLv3");

		X509TrustManager trustManager = new X509TrustManager(){
			public void checkClientTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}
			public void checkServerTrusted(
					java.security.cert.X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException{
			}
			public java.security.cert.X509Certificate[] getAcceptedIssuers(){
				return null;
			}
		};

		sc.init(null, new TrustManager[]{trustManager}, null);

		return sc;
	}
	public static CloseableHttpClient GetClient(boolean crypted) throws Exception{
		HttpClientBuilder builder = HttpClients.custom();

		builder.setDefaultRequestConfig(RequestConfig.custom().setSocketTimeout(timeout).setConnectTimeout(timeout).setConnectionRequestTimeout(timeout).build());

		if (crypted){
			SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(CreateIgnoreVerifySSL());
			PoolingHttpClientConnectionManager connmgr = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", factory).build());

			return builder.setConnectionManager(connmgr).setConnectionTimeToLive(timeout, TimeUnit.MILLISECONDS).build();
		}

		return builder.setConnectionTimeToLive(timeout, TimeUnit.MILLISECONDS).build();
	}

	public static byte[] EntityToBytes(HttpEntity entity) throws IOException{
		if (entity == null) return null;

		if (entity.getContentLength() < 4096) return EntityUtils.toByteArray(entity);

		int len = 0;
		byte[] tmp = new byte[4096];
		InputStream reader = entity.getContent();
		ByteArrayBuffer buffer = new ByteArrayBuffer(4096);

		while ((len = reader.read(tmp)) >= 0){
			if (len > 0) buffer.append(tmp, 0, len);
		}

		return buffer.toByteArray();
	}
	public static String GetString(String url){
		return GetString(url, (String)(null), null, charset);
	}
	public static String GetString(String url, Map<String, String> data){
		return GetString(url, data, null, charset);
	}
	public static String GetString(String url, Map<String, String> data, Map<String, String> head){
		return GetString(url, data, head, charset);
	}
	public static String GetString(String url, Map<String, String> data, Map<String, String> head, String charset){
		if (charset == null || charset.isEmpty()) charset = GetCharset();

		String rs = null;
		byte[] bs = GetResult(url, data, head, charset);

		if (bs == null) return rs;

		try{
			rs = new String(bs, charset);
		}
		catch(Exception e){
		}

		return rs;
	}
	public static byte[] GetResult(String url){
		return GetResult(url, (String)(null), null, charset);
	}
	public static byte[] GetResult(String url, Map<String, String> data){
		return GetResult(url, data, null, charset);
	}
	public static byte[] GetResult(String url, Map<String, String> data, Map<String, String> head){
		return GetResult(url, data, head, charset);
	}
	public static byte[] GetResult(String url, Map<String, String> data, Map<String, String> head, String charset){
		if (data == null) return GetResult(url, data, head, charset);
		
		String msg = "";
		
		for (Map.Entry<String, String> item : data.entrySet()){
			msg += "&" + item.getKey() + "=" + Encode(item.getValue(), charset);
		}

		if (msg.length() > 0) msg = msg.substring(1);

		return GetResult(url, msg, head, charset);
	}
	public static String GetString(String url, String data){
		return GetString(url, data, null, charset);
	}
	public static String GetString(String url, String data, Map<String, String> head){
		return GetString(url, data, head, charset);
	}
	public static String GetString(String url, String data, Map<String, String> head, String charset){
		if (charset == null || charset.isEmpty()) charset = GetCharset();

		String rs = null;
		byte[] bs = GetResult(url, data, head, charset);

		if (bs == null) return rs;

		try{
			rs = new String(bs, charset);
		}
		catch(Exception e){
		}

		return rs;
	}
	public static byte[] GetResult(String url, String data){
		return GetResult(url, data, null, charset);
	}
	public static byte[] GetResult(String url, String data, Map<String, String> head){
		return GetResult(url, data, head, charset);
	}
	public static byte[] GetResult(String url, String data, Map<String, String> head, String charset){
		if (charset == null || charset.isEmpty()) charset = GetCharset();

		for (int i = 0; i < 3; i++){
			CloseableHttpClient http = null;
			CloseableHttpResponse response = null;

			errno.set(BaseException.SENDFAIL.getErrorCode());

			try{
				http = GetClient(url.startsWith("https"));

				if (data == null){
					HttpGet get = new HttpGet(url);

					if (head != null && head.size() > 0){
						for (Map.Entry<String, String> item : head.entrySet()){
							get.setHeader(item.getKey().trim(), item.getValue().trim());
						}
					}

					errno.set(BaseException.RECVFAIL.getErrorCode());

					response = http.execute(get);
				}
				else{
					HttpPost post = new HttpPost(url);
					StringEntity entity = new StringEntity(data, charset);

					if (head != null && head.size() > 0){
						for (Map.Entry<String, String> item : head.entrySet()){
							post.setHeader(item.getKey().trim(), item.getValue().trim());
						}
					}

					post.setEntity(entity);

					errno.set(BaseException.RECVFAIL.getErrorCode());

					response = http.execute(post);
				}

				int code = response.getStatusLine().getStatusCode();

				errno.set(BaseException.OK.getErrorCode());
				status.set(code);

				if (code == 200) return EntityToBytes(response.getEntity());

				org.apache.http.Header tmp = response.getFirstHeader("Location");

				if (tmp == null) tmp = response.getFirstHeader("location");

				if (tmp == null) return EntityToBytes(response.getEntity());

				url = tmp.getValue();

				if (url.isEmpty()) return EntityToBytes(response.getEntity());
			}
			catch(java.net.UnknownHostException e){
				errno.set(BaseException.SENDFAIL.getErrorCode());
			}
			catch(java.net.ConnectException e){
				errno.set(BaseException.SENDFAIL.getErrorCode());
			}
			catch(Exception e){
				e.printStackTrace();
			}
			finally{
				Utils.Close(response);
				Utils.Close(http);
			}
		}

		return null;
	}
}